﻿#region File Description
/****************************************************************************
*                                                                           *
* Gameplay Screen.                                                          *
*                                                                           *
* Copyright (c) Ionescu Marius. All rights reserved(2010).                  *
*                                                                           *
****************************************************************************/
#endregion

#region Using Statements
using System;
using System.Threading;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
#endregion

namespace XNA_FRAMEWORK
{
	class XNAGameplayScreen : XNAGameScreen
	{
		#region Fields

		ContentManager content;
		SpriteFont gameFont;
		Vector2 playerPosition = new Vector2(20, 20);

		GameObject terrain = new GameObject();
		GameObject missileLauncherBase = new GameObject();
		GameObject missileLauncherHead = new GameObject();
		const int numMissiles = 20;
		GameObject[] missiles;
		GamePadState previousState;
#if !XBOX
		KeyboardState previousKeyboardState;
#endif
		const float launcherHeadMuzzleOffset = 20.0f;
		const float missilePower = 20.0f;
		AudioEngine audioEngine;
		SoundBank soundBank;
		WaveBank waveBank;
		Random r = new Random();
		const int numEnemyShips = 3;
		GameObject[] enemyShips;
		Vector3 shipMinPosition = new Vector3(-2000.0f, 300.0f, -6000.0f);
		Vector3 shipMaxPosition = new Vector3(2000.0f, 800.0f, -4000.0f);
		const float shipMinVelocity = 5.0f;
		const float shipMaxVelocity = 10.0f;
		Vector3 cameraPosition = new Vector3(0.0f, 60.0f, 160.0f);
		Vector3 cameraLookAt = new Vector3(0.0f, 50.0f, 0.0f);
		Matrix cameraProjectionMatrix;
		Matrix cameraViewMatrix;
		static int score = 0;

		#endregion

		#region Initialization

		public XNAGameplayScreen()
		{
			TransitionOnTime = TimeSpan.FromSeconds(1.5);
			TransitionOffTime = TimeSpan.FromSeconds(0.5);
		}

		public override void LoadContent()
		{
			if (content == null)
				content = new ContentManager(ScreenManager.Game.Services, "Content");

			audioEngine = new AudioEngine("Content\\Audio\\TutorialAudio.xgs");
			waveBank = new WaveBank(audioEngine, "Content\\Audio\\Wave Bank.xwb");
			soundBank = new SoundBank(audioEngine, "Content\\Audio\\Sound Bank.xsb");

			cameraViewMatrix = Matrix.CreateLookAt(
				cameraPosition,
				cameraLookAt,
				Vector3.Up);

			cameraProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(
				MathHelper.ToRadians(45.0f),
				ScreenManager.GraphicsDevice.Viewport.AspectRatio,
				1.0f,
				10000.0f);

			terrain.model = content.Load<Model>(
				"Models\\terrain");

			missileLauncherBase.model = content.Load<Model>(
				"Models\\launcher_base");
			missileLauncherBase.scale = 0.2f;

			missileLauncherHead.model = content.Load<Model>(
				"Models\\launcher_head");
			missileLauncherHead.scale = 0.2f;
			missileLauncherHead.position = missileLauncherBase.position +
				new Vector3(0.0f, 20.0f, 0.0f);

			missiles = new GameObject[numMissiles];
			for (int i = 0; i < numMissiles; i++)
			{
				missiles[i] = new GameObject();
				missiles[i].model =
					content.Load<Model>("Models\\missile");
				missiles[i].scale = 3.0f;
			}

			enemyShips = new GameObject[numEnemyShips];
			for (int i = 0; i < numEnemyShips; i++)
			{
				enemyShips[i] = new GameObject();
				enemyShips[i].model = content.Load<Model>(
					"Models\\enemy");
				enemyShips[i].scale = 0.1f;
				enemyShips[i].rotation = new Vector3(
					0.0f, MathHelper.Pi, 0.0f);
			}

			gameFont = content.Load<SpriteFont>("gamefont");

			Thread.Sleep(1000);
			ScreenManager.Game.ResetElapsedTime();
		}

		public override void UnloadContent()
		{
			content.Unload();
		}

		#endregion

		#region Update and Draw

		public override void Update(GameTime gameTime, bool otherScreenHasFocus,
													   bool coveredByOtherScreen)
		{
			if (IsActive)
			{
				GamePadState gamePadState = GamePad.GetState(PlayerIndex.One);

				missileLauncherHead.rotation.Y -=
			   gamePadState.ThumbSticks.Left.X * 0.1f;

				missileLauncherHead.rotation.X +=
					gamePadState.ThumbSticks.Left.Y * 0.1f;

#if !XBOX
				KeyboardState keyboardState = Keyboard.GetState();
				if (keyboardState.IsKeyDown(Keys.Left))
				{
					missileLauncherHead.rotation.Y += 0.05f;
				}
				if (keyboardState.IsKeyDown(Keys.Right))
				{
					missileLauncherHead.rotation.Y -= 0.05f;
				}
				if (keyboardState.IsKeyDown(Keys.Up))
				{
					missileLauncherHead.rotation.X += 0.05f;
				}
				if (keyboardState.IsKeyDown(Keys.Down))
				{
					missileLauncherHead.rotation.X -= 0.05f;
				}
#endif

				missileLauncherHead.rotation.Y = MathHelper.Clamp(
					missileLauncherHead.rotation.Y,
					-MathHelper.PiOver4, MathHelper.PiOver4);

				missileLauncherHead.rotation.X = MathHelper.Clamp(
					missileLauncherHead.rotation.X,
					0, MathHelper.PiOver4);

				if (gamePadState.Buttons.A == ButtonState.Pressed &&
					previousState.Buttons.A == ButtonState.Released)
				{
					FireMissile();
				}

#if !XBOX
				if (keyboardState.IsKeyDown(Keys.Space) &&
					previousKeyboardState.IsKeyUp(Keys.Space))
				{
					FireMissile();
				}
#endif

				UpdateMissiles();

				audioEngine.Update();

				UpdateEnemyShips();

				previousState = gamePadState;
#if !XBOX
				previousKeyboardState = keyboardState;
#endif
			}

			base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
		}

		public override void HandleInput(XNAInput input)
		{
			if (input == null)
				throw new ArgumentNullException("input");

			int playerIndex = (int)ControllingPlayer.Value;

			KeyboardState keyboardState = input.CurrentKeyboardStates[playerIndex];
			GamePadState gamePadState = input.CurrentGamePadStates[playerIndex];

			bool gamePadDisconnected = !gamePadState.IsConnected &&
									   input.GamePadWasConnected[playerIndex];

			if (input.IsPauseGame(ControllingPlayer) || gamePadDisconnected)
			{
				ScreenManager.AddScreen(new XNAPauseMenuScreen(), ControllingPlayer);
			}
			else
			{

			}
		}

		void FireMissile()
		{
			foreach (GameObject missile in missiles)
			{
				if (!missile.alive)
				{
					soundBank.PlayCue("missilelaunch");

					missile.velocity = GetMissileMuzzleVelocity();
					missile.position = GetMissileMuzzlePosition();
					missile.rotation = missileLauncherHead.rotation;
					missile.alive = true;
					break;
				}
			}
		}

		Vector3 GetMissileMuzzleVelocity()
		{
			Matrix rotationMatrix =
				Matrix.CreateFromYawPitchRoll(
				missileLauncherHead.rotation.Y,
				missileLauncherHead.rotation.X,
				0);

			return Vector3.Normalize(
				Vector3.Transform(Vector3.Forward,
				rotationMatrix)) * missilePower;
		}

		Vector3 GetMissileMuzzlePosition()
		{
			return missileLauncherHead.position +
				(Vector3.Normalize(
				GetMissileMuzzleVelocity()) *
				launcherHeadMuzzleOffset);
		}

		void UpdateMissiles()
		{
			foreach (GameObject missile in missiles)
			{
				if (missile.alive)
				{
					missile.position += missile.velocity;
					if (missile.position.Z < -6000.0f)
					{
						missile.alive = false;
					}
					else
					{
						TestCollision(missile);
					}
				}
			}
		}

		void UpdateEnemyShips()
		{
			foreach (GameObject ship in enemyShips)
			{
				if (ship.alive)
				{
					ship.position += ship.velocity;
					if (ship.position.Z > 500.0f)
					{
						ship.alive = false;
					}
				}
				else
				{
					ship.alive = true;
					ship.position = new Vector3(
						MathHelper.Lerp(
						shipMinPosition.X,
						shipMaxPosition.X,
						(float)r.NextDouble()),

						MathHelper.Lerp(
						shipMinPosition.Y,
						shipMaxPosition.Y,
						(float)r.NextDouble()),

						MathHelper.Lerp(
						shipMinPosition.Z,
						shipMaxPosition.Z,
						(float)r.NextDouble()));

					ship.velocity = new Vector3(
						0.0f,
						0.0f,
						MathHelper.Lerp(shipMinVelocity,
						shipMaxVelocity, (float)r.NextDouble()));
				}
			}
		}

		void TestCollision(GameObject missile)
		{
			BoundingSphere missilesphere =
				missile.model.Meshes[0].BoundingSphere;
			missilesphere.Center = missile.position;
			missilesphere.Radius *= missile.scale;

			foreach (GameObject ship in enemyShips)
			{
				if (ship.alive)
				{
					BoundingSphere shipsphere =
						ship.model.Meshes[0].BoundingSphere;
					shipsphere.Center = ship.position;
					shipsphere.Radius *= ship.scale;

					if (shipsphere.Intersects(missilesphere))
					{
						soundBank.PlayCue("explosion");
						missile.alive = false;
						ship.alive = false;
						score++;
						break;
					}
				}
			}
		}

		public override void Draw(GameTime gameTime)
		{
			ScreenManager.GraphicsDevice.Clear(ClearOptions.Target,
											   Color.CornflowerBlue, 0, 0);

			DrawGameObject(terrain);
			DrawGameObject(missileLauncherBase);
			DrawGameObject(missileLauncherHead);

			foreach (GameObject missile in missiles)
			{
				if (missile.alive)
				{
					DrawGameObject(missile);
				}
			}

			foreach (GameObject enemyship in enemyShips)
			{
				if (enemyship.alive)
				{
					DrawGameObject(enemyship);
				}
			}

			SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
			spriteBatch.Begin();
			spriteBatch.DrawString(gameFont, "Score: " + Convert.ToString(score), playerPosition, Color.Green);
			spriteBatch.End();

			if (TransitionPosition > 0)
				ScreenManager.FadeBackBufferToBlack(255 - TransitionAlpha);
		}

		void DrawGameObject(GameObject gameobject)
		{
			foreach (ModelMesh mesh in gameobject.model.Meshes)
			{
				foreach (BasicEffect effect in mesh.Effects)
				{
					effect.EnableDefaultLighting();
					effect.PreferPerPixelLighting = true;

					effect.World =
						Matrix.CreateFromYawPitchRoll(
						gameobject.rotation.Y,
						gameobject.rotation.X,
						gameobject.rotation.Z) *

						Matrix.CreateScale(gameobject.scale) *

						Matrix.CreateTranslation(gameobject.position);

					effect.Projection = cameraProjectionMatrix;
					effect.View = cameraViewMatrix;
				}
				mesh.Draw();
			}
		}

		#endregion

	}

	class GameObject
	{
		public Model model = null;
		public Vector3 position = Vector3.Zero;
		public Vector3 rotation = Vector3.Zero;
		public float scale = 1.0f;
		public Vector3 velocity = Vector3.Zero;
		public bool alive = false;
	}
}
